/**
 * <p>IJPay 让支付触手可及，封装了微信支付、支付宝支付、银联支付常用的支付方式以及各种常用的接口。</p>
 *
 * <p>不依赖任何第三方 mvc 框架，仅仅作为工具使用简单快速完成支付模块的开发，可轻松嵌入到任何系统里。 </p>
 *
 * <p>IJPay 交流群: 723992875</p>
 *
 * <p>Node.js 版: https://gitee.com/javen205/TNWX</p>
 *
 * <p>TODO</p>
 *
 * @author Javen
 */
package com.ruoyi.project.merchant.controller;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.file.FileWriter;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.FileUtils;
import com.ruoyi.common.utils.JsonTool;
import com.ruoyi.framework.config.WxPayV3Bean;
import com.ruoyi.project.merchant.domain.BillOrderDetail;
import com.ruoyi.project.merchant.wx.core.enums.RequestMethod;
import com.ruoyi.project.merchant.wx.core.kit.AesUtil;
import com.ruoyi.project.merchant.wx.core.kit.PayKit;
import com.ruoyi.project.merchant.wx.core.kit.RsaKit;
import com.ruoyi.project.merchant.wx.core.kit.WxPayKit;
import com.ruoyi.project.merchant.wx.wxpay.WxPayApi;
import com.ruoyi.project.merchant.wx.wxpay.enums.WxApiType;
import com.ruoyi.project.merchant.wx.wxpay.enums.WxDomain;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStreamReader;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Controller
@RequestMapping("/v3")
public class WxPayV3Controller {
    private Logger log = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private WxPayV3Bean wxPayV3Bean;

    String serialNo;
    String platSerialNo;


    @RequestMapping("")
    @ResponseBody
    public String index() {
        log.info(wxPayV3Bean.toString());
        return ("欢迎使用 IJPay 中的微信支付 Api-v3 -By Javen  <br/><br>  交流群：723992875");
    }

    @RequestMapping("/getSerialNumber")
    @ResponseBody
    public String serialNumber() {
        return getSerialNumber();
    }

    @RequestMapping("/getPlatSerialNumber")
    @ResponseBody
    public String platSerialNumber() {
        return getPlatSerialNumber();
    }

    private String getSerialNumber() {
        if (StrUtil.isEmpty(serialNo)) {
            // 获取证书序列号
            X509Certificate certificate = PayKit.getCertificate(FileUtil.getInputStream(wxPayV3Bean.getCertPath()));
            serialNo = certificate.getSerialNumber().toString(16).toUpperCase();

//            System.out.println("输出证书信息:\n" + certificate.toString());
//            // 输出关键信息，截取部分并进行标记
//            System.out.println("证书序列号:" + certificate.getSerialNumber().toString(16));
//            System.out.println("版本号:" + certificate.getVersion());
//            System.out.println("签发者：" + certificate.getIssuerDN());
//            System.out.println("有效起始日期：" + certificate.getNotBefore());
//            System.out.println("有效终止日期：" + certificate.getNotAfter());
//            System.out.println("主体名：" + certificate.getSubjectDN());
//            System.out.println("签名算法：" + certificate.getSigAlgName());
//            System.out.println("签名：" + certificate.getSignature().toString());
        }
        System.out.println("serialNo:" + serialNo);
        return serialNo;
    }

    private String getPlatSerialNumber() {
        if (StrUtil.isEmpty(platSerialNo)) {
            // 获取平台证书序列号
            X509Certificate certificate = PayKit.getCertificate(FileUtil.getInputStream(wxPayV3Bean.getPlatformCertPath()));
            platSerialNo = certificate.getSerialNumber().toString(16).toUpperCase();
        }
        System.out.println("platSerialNo:" + platSerialNo);
        return platSerialNo;
    }

    @RequestMapping("/platformCert")
    @ResponseBody
    public String platformCert() {
        try {
            String associatedData = "certificate";
            String nonce = "80d28946a64a";
            String cipherText = "DwAqW4+4TeUaOEylfKEXhw+XqGh/YTRhUmLw/tBfQ5nM9DZ9d+9aGEghycwV1jwo52vXb/t6ueBvBRHRIW5JgDRcXmTHw9IMTrIK6HxTt2qiaGTWJU9whsF+GGeQdA7gBCHZm3AJUwrzerAGW1mclXBTvXqaCl6haE7AOHJ2g4RtQThi3nxOI63/yc3WaiAlSR22GuCpy6wJBfljBq5Bx2xXDZXlF2TNbDIeodiEnJEG2m9eBWKuvKPyUPyClRXG1fdOkKnCZZ6u+ipb4IJx28n3MmhEtuc2heqqlFUbeONaRpXv6KOZmH/IdEL6nqNDP2D7cXutNVCi0TtSfC7ojnO/+PKRu3MGO2Z9q3zyZXmkWHCSms/C3ACatPUKHIK+92MxjSQDc1E/8faghTc9bDgn8cqWpVKcL3GHK+RfuYKiMcdSkUDJyMJOwEXMYNUdseQMJ3gL4pfxuQu6QrVvJ17q3ZjzkexkPNU4PNSlIBJg+KX61cyBTBumaHy/EbHiP9V2GeM729a0h5UYYJVedSo1guIGjMZ4tA3WgwQrlpp3VAMKEBLRJMcnHd4pH5YQ/4hiUlHGEHttWtnxKFwnJ6jHr3OmFLV1FiUUOZEDAqR0U1KhtGjOffnmB9tymWF8FwRNiH2Tee/cCDBaHhNtfPI5129SrlSR7bZc+h7uzz9z+1OOkNrWHzAoWEe3XVGKAywpn5HGbcL+9nsEVZRJLvV7aOxAZBkxhg8H5Fjt1ioTJL+qXgRzse1BX1iiwfCR0fzEWT9ldDTDW0Y1b3tb419MhdmTQB5FsMXYOzqp5h+Tz1FwEGsa6TJsmdjJQSNz+7qPSg5D6C2gc9/6PkysSu/6XfsWXD7cQkuZ+TJ/Xb6Q1Uu7ZB90SauA8uPQUIchW5zQ6UfK5dwMkOuEcE/141/Aw2rlDqjtsE17u1dQ6TCax/ZQTDQ2MDUaBPEaDIMPcgL7fCeijoRgovkBY92m86leZvQ+HVbxlFx5CoPhz4a81kt9XJuEYOztSIKlm7QNfW0BvSUhLmxDNCjcxqwyydtKbLzA+EBb2gG4ORiH8IOTbV0+G4S6BqetU7RrO+/nKt21nXVqXUmdkhkBakLN8FUcHygyWnVxbA7OI2RGnJJUnxqHd3kTbzD5Wxco4JIQsTOV6KtO5c960oVYUARZIP1SdQhqwELm27AktEN7kzg/ew/blnTys/eauGyw78XCROb9F1wbZBToUZ7L+8/m/2tyyyqNid+sC9fYqJoIOGfFOe6COWzTI/XPytCHwgHeUxmgk7NYfU0ukR223RPUOym6kLzSMMBKCivnNg68tbLRJHEOpQTXFBaFFHt2qpceJpJgw5sKFqx3eQnIFuyvA1i8s2zKLhULZio9hpsDJQREOcNeHVjEZazdCGnbe3Vjg7uqOoVHdE/YbNzJNQEsB3/erYJB+eGzyFwFmdAHenG5RE6FhCutjszwRiSvW9F7wvRK36gm7NnVJZkvlbGwh0UHr0pbcrOmxT81xtNSvMzT0VZNLTUX2ur3AGLwi2ej8BIC0H41nw4ToxTnwtFR1Xy55+pUiwpB7JzraA08dCXdFdtZ72Tw/dNBy5h1P7EtQYiKzXp6rndfOEWgNOsan7e1XRpCnX7xoAkdPvy40OuQ5gNbDKry5gVDEZhmEk/WRuGGaX06CG9m7NfErUsnQYrDJVjXWKYuARd9R7W0aa5nUXqz/Pjul/LAatJgWhZgFBGXhNr9iAoade/0FPpBj0QWa8SWqKYKiOqXqhfhppUq35FIa0a1Vvxcn3E38XYpVZVTDEXcEcD0RLCu/ezdOa6vRcB7hjgXFIRZQAka0aXnQxwOZwE2Rt3yWXqc+Q1ah2oOrg8Lg3ETc644X9QP4FxOtDwz/A==";

            AesUtil aesUtil = new AesUtil(wxPayV3Bean.getApiKey3().getBytes(StandardCharsets.UTF_8));
            // 平台证书密文解密
            // encrypt_certificate 中的  associated_data nonce  ciphertext
            String publicKey = aesUtil.decryptToString(
                    associatedData.getBytes(StandardCharsets.UTF_8),
                    nonce.getBytes(StandardCharsets.UTF_8),
                    cipherText
            );
            // 保存证书
            FileWriter writer = new FileWriter(wxPayV3Bean.getPlatformCertPath());
            writer.write(publicKey);
            // 获取平台证书序列号
            X509Certificate certificate = PayKit.getCertificate(new ByteArrayInputStream(publicKey.getBytes()));
            return certificate.getSerialNumber().toString(16).toUpperCase();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @RequestMapping("/get")
    @ResponseBody
    public String v3Get() {
        // 获取平台证书列表
        try {
            Map<String, Object> result = WxPayApi.v3Execution(
                    RequestMethod.GET,
                    WxDomain.CHINA.toString(),
                    WxApiType.GET_CERTIFICATES.toString(),
                    wxPayV3Bean.getMchId(),
                    getSerialNumber(),
                    wxPayV3Bean.getKeyPath(),
                    ""
            );

            String serialNumber = MapUtil.getStr(result, "serialNumber");
            String body = MapUtil.getStr(result, "body");
            int status = MapUtil.getInt(result, "status");

            System.out.println("serialNumber:" + serialNumber);
            System.out.println("status:" + status);
            // 根据证书序列号查询对应的证书来验证签名结果
            boolean verifySignature = WxPayKit.verifySignature(result, wxPayV3Bean.getPlatformCertPath());

            System.out.println("verifySignature:" + verifySignature + "\nbody:" + body);

            return body;

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @RequestMapping("/getParams")
    @ResponseBody
    public String payScoreUserServiceState() {
        try {
            Map<String, String> params = new HashMap<>();
            params.put("service_id", "500001");
            params.put("appid", "wxd678efh567hg6787");
            params.put("openid", "oUpF8uMuAJO_M2pxb1Q9zNjWeS6o");

            Map<String, Object> result = WxPayApi.v3Execution(
                    RequestMethod.GET,
                    WxDomain.CHINA.toString(),
                    WxApiType.PAY_SCORE_USER_SERVICE_STATE.toString(),
                    wxPayV3Bean.getMchId(),
                    getSerialNumber(),
                    wxPayV3Bean.getKeyPath(),
                    params
            );
            System.out.println(result);
            return JSONUtil.toJsonStr(result);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @RequestMapping("/delete")
    @ResponseBody
    public String v3Delete() {
        // 创建/查询/更新/删除投诉通知回调
        try {
            HashMap<String, String> hashMap = new HashMap<>();
            hashMap.put("url", "https://qq.com");
            Map<String, Object> result = WxPayApi.v3Execution(
                    RequestMethod.POST,
                    WxDomain.CHINA.toString(),
                    WxApiType.MERCHANT_SERVICE_COMPLAINTS_NOTIFICATIONS.toString(),
                    wxPayV3Bean.getMchId(),
                    getSerialNumber(),
                    wxPayV3Bean.getKeyPath(),
                    JSONUtil.toJsonStr(hashMap)
            );
            System.out.println(result);

            result = WxPayApi.v3Execution(
                    RequestMethod.DELETE,
                    WxDomain.CHINA.toString(),
                    WxApiType.MERCHANT_SERVICE_COMPLAINTS_NOTIFICATIONS.toString(),
                    wxPayV3Bean.getMchId(),
                    getSerialNumber(),
                    wxPayV3Bean.getKeyPath(),
                    ""
            );
            // 根据证书序列号查询对应的证书来验证签名结果
            boolean verifySignature = WxPayKit.verifySignature(result, wxPayV3Bean.getPlatformCertPath());
            System.out.println("verifySignature:" + verifySignature);
            // 如果返回的为 204 表示删除成功
            System.out.println(result);
            return JSONUtil.toJsonStr(result);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @PostMapping("/upload")
    @ResponseBody
    public AjaxResult v3Upload(@RequestParam MultipartFile mfile) {
        // v3 接口上传文件
        try {
            String filePath = "D:\\cardphone\\公司营业执照.jpg";
            File file = FileUtils.multipartFileToFile(mfile);
          //  File file = FileUtil.newFile(filePath);
            String sha256 = SecureUtil.sha256(file);

            HashMap<Object, Object> map = new HashMap<>();
            map.put("filename", file.getName());
            map.put("sha256", sha256);
            String body = JSONUtil.toJsonStr(map);

           log.info("图片上传文件处理:"+body);

            Map<String, Object> result = WxPayApi.v3Upload(
                    WxDomain.CHINA.toString(),
                    WxApiType.MERCHANT_UPLOAD_MEDIA.toString(),
                    wxPayV3Bean.getMchId(),
                    getSerialNumber(),
                    wxPayV3Bean.getKeyPath(),
                    body,
                    file
            );
            // 根据证书序列号查询对应的证书来验证签名结果
            boolean verifySignature = WxPayKit.verifySignature(result, wxPayV3Bean.getPlatformCertPath());
            log.info("签名验签结果verifySignature:" + verifySignature);
          //  JSONObject jsonObject=JSONUtil.parseObj(result.get("body"));
            if (MapUtil.getInt(result, "status")==200){
                return  AjaxResult.success(result.get("body"));
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return AjaxResult.error();
    }

    @RequestMapping("/post")
    @ResponseBody
    public String payGiftActivity() {
        // 支付有礼-终止活动
        try {
            String urlSuffix = String.format(WxApiType.PAY_GIFT_ACTIVITY_TERMINATE.toString(), "10028001");
            System.out.println(urlSuffix);
            Map<String, Object> result = WxPayApi.v3Execution(
                    RequestMethod.POST,
                    WxDomain.CHINA.toString(),
                    urlSuffix,
                    wxPayV3Bean.getMchId(),
                    getSerialNumber(),
                    wxPayV3Bean.getKeyPath(),
                    ""
            );
            System.out.println(result);
            return JSONUtil.toJsonStr(result);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @RequestMapping("/sensitive")
    @ResponseBody
    public String sensitive() {
        // 带有敏感信息接口,需要平台证书
        try {
            String body = "处理请求参数";

            Map<String, Object> result = WxPayApi.v3Execution(
                    RequestMethod.POST,
                    WxDomain.CHINA.toString(),
                    WxApiType.APPLY_4_SUB.toString(),
                    wxPayV3Bean.getMchId(),
                    getSerialNumber(),
                    getPlatSerialNumber(),
                    wxPayV3Bean.getKeyPath(),
                    body
            );
            System.out.println(result);
            return JSONUtil.toJsonStr(result);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    //app合单支付
    @RequestMapping("/combineApp/{type}")
    @ResponseBody
    public Map<String, Object> combineTransactionsApp(@RequestBody String body,@PathVariable("type") String type) {
        // 带有敏感信息接口,需要平台证书
        String urlSuffix = "";
        try {

            if ("app".equals(type)) {
                urlSuffix = WxApiType.COMBINE_TRANSACTIONS_APP.toString();
                Map<String, Object> result = WxPayApi.v3Execution(
                        RequestMethod.POST,
                        WxDomain.CHINA.toString(),
                        urlSuffix,
                        wxPayV3Bean.getMchId(),
                        getSerialNumber(),
                        getPlatSerialNumber(),
                        wxPayV3Bean.getKeyPath(),
                        body
                );
                Map<String, Object> resultSing=new HashMap<>();
                String noncestr=WxPayKit.generateStr();
                String timestamp = Long.toString(System.currentTimeMillis() / 1000);

                if (MapUtil.getInt(result, "status")==200){
                    JSONObject jsonObject=JSONUtil.parseObj(result.get("body"));
                    String prepay_id = MapUtil.getStr(jsonObject,"prepay_id");
                    resultSing.put("appid", wxPayV3Bean.getAppId());
                    resultSing.put("partnerid", wxPayV3Bean.getMchId());
                    resultSing.put("prepayid", prepay_id);
                    resultSing.put("package", "Sign=WXPay");
                    resultSing.put("noncestr", noncestr);
                    resultSing.put("timestamp", timestamp);

                    //签名计算
                    StringBuffer sb = new StringBuffer();
                    sb.append(wxPayV3Bean.getAppId()).append("\n");
                    sb.append(timestamp).append("\n");
                    sb.append(noncestr).append("\n");
                    sb.append(prepay_id).append("\n");
                    // 获取商户私钥
                    PrivateKey privateKey = PayKit.getPrivateKey(wxPayV3Bean.getKeyPath());
                    // 生成签名
                    String signature = RsaKit.encryptByPrivateKey(sb.toString(), privateKey);
                    System.out.println("获得签名值:"+signature);
                    resultSing.put("paysign", signature);
                    //兼容v2版本差异
                    resultSing.put("mch_id",wxPayV3Bean.getMchId());
                    resultSing.put("prepay_id",prepay_id);
                    resultSing.put("nonce_str",noncestr);
                    resultSing.put("sign",signature);

                    return  resultSing;
                }
                return null;
            }else if (("wechat".equals(type))){
                urlSuffix = WxApiType.COMBINE_TRANSACTIONS_JS.toString();
                Map<String, Object> result = WxPayApi.v3Execution(
                        RequestMethod.POST,
                        WxDomain.CHINA.toString(),
                        urlSuffix,
                        wxPayV3Bean.getMchId(),
                        getSerialNumber(),
                        getPlatSerialNumber(),
                        wxPayV3Bean.getKeyPath(),
                        body
                );
                Map<String, Object> resultSing=new HashMap<>();
                if (MapUtil.getInt(result, "status")==200) {
                    JSONObject jsonObject = JSONUtil.parseObj(result.get("body"));
                    System.out.println(JsonTool.mapToJson(result.get("body")));
                    String prepay_id = MapUtil.getStr(jsonObject, "prepay_id");
                    System.out.println(prepay_id);
                    resultSing.put("prepay_id", prepay_id);
                    return resultSing;
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    @RequestMapping("/sign")
    @ResponseBody
    public Map<String, Object> sign(@RequestBody Map<String,Object> body) {
        // 带有敏感信息接口,需要平台证书
        try {
                Map<String, Object> resultSing=new HashMap<>();
                String noncestr=WxPayKit.generateStr();
                String timestamp = Long.toString(System.currentTimeMillis() / 1000);
                String appid=body.get("appId").toString();
                String packages=body.get("package").toString();
                resultSing.put("appId", appid);
                resultSing.put("noncestr", noncestr);
                resultSing.put("package", packages);
                resultSing.put("timestamp", timestamp);
                resultSing.put("signType", "RSA");

                //签名计算
                StringBuffer sb = new StringBuffer();
                sb.append(appid).append("\n");
                sb.append(timestamp).append("\n");
                sb.append(noncestr).append("\n");
                sb.append(packages).append("\n");
                // 获取商户私钥
                PrivateKey privateKey = PayKit.getPrivateKey(wxPayV3Bean.getKeyPath());
                // 生成签名
                String signature = RsaKit.encryptByPrivateKey(sb.toString(), privateKey);
                resultSing.put("paysign", signature);
                return resultSing;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }



    /**
     * @Author hanguanglei
     * @Description //合单查询订单API
     * @Date 11:31 2020/4/8
     * @Param
     * @param combineOutTradeNo  合单商户订单号
     * @return java.lang.String
     **/
    @RequestMapping("/combineAppQueryOrder")
    @ResponseBody
    public JSONObject combineTransactionsAppOrder(@RequestParam String combineOutTradeNo) {

        try {
            String urlSuffix = String.format(WxApiType.COMBINE_TRANSACTIONS_QUERY.toString(), combineOutTradeNo);
            Map<String, Object> result = WxPayApi.v3Execution(
                    RequestMethod.GET,
                    WxDomain.CHINA.toString(),
                    urlSuffix,
                    wxPayV3Bean.getMchId(),
                    getSerialNumber(),
                    getPlatSerialNumber(),
                    wxPayV3Bean.getKeyPath(),
                    ""
            );
            if (MapUtil.getInt(result, "status")==200) {
                JSONObject jsonObject = JSONUtil.parseObj(result.get("body"));
                return jsonObject;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }



    /**
     * @Author hanguanglei
     * @Description //合单订单关闭API
     * @Date 11:31 2020/4/8
     * @Param
     * @param combineOutTradeNo  合单商户订单号
     * @return java.lang.String
     **/
    @RequestMapping("/combineAppOrderClose")
    @ResponseBody
    public JSONObject combineTransactionsAppOrderClose(@RequestParam String combineOutTradeNo) {

        try {
            String urlSuffix = String.format(WxApiType.COMBINE_TRANSACTIONS_CLOSE.toString(), combineOutTradeNo);
            Map<String, Object> result = WxPayApi.v3Execution(
                    RequestMethod.GET,
                    WxDomain.CHINA.toString(),
                    urlSuffix,
                    wxPayV3Bean.getMchId(),
                    getSerialNumber(),
                    getPlatSerialNumber(),
                    wxPayV3Bean.getKeyPath(),
                    ""
            );
            if (MapUtil.getInt(result, "status")==200) {
                JSONObject jsonObject = JSONUtil.parseObj(result.get("body"));
                return jsonObject;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * @Author hanguanglei
     * @Description //订单支付回调API
     * @Date 11:31 2020/4/8
     * @Param
     * @param body  合单商户订单号
     * @return java.lang.String
     **/
    @RequestMapping("/OrderNotify")
    @ResponseBody
    public ResponseEntity<Map<String,Object>> OrderNotify( @RequestBody Map<String,Object> body) {

        try {
            MapUtil.getStr(body, "event_type");
             String event_type=MapUtil.getStr(body, "event_type");

             if ("TRANSACTION.SUCCESS".equals(event_type)){
                 String ob= JsonTool.mapToJson(body.get("resource"));
                 Map<String,Object> resource= JsonTool.jsonStringToMapObjectStr(ob);
                 String associatedData = MapUtil.getStr(resource,"associated_data");
                 String nonce = MapUtil.getStr(resource,"nonce");
                 String cipherText = MapUtil.getStr(resource,"ciphertext");
                 AesUtil aesUtil = new AesUtil(wxPayV3Bean.getApiKey3().getBytes(StandardCharsets.UTF_8));
                 // 平台证书密文解密数据
                 String data = aesUtil.decryptToString(
                         associatedData.getBytes(StandardCharsets.UTF_8),
                         nonce.getBytes(StandardCharsets.UTF_8),
                         cipherText
                 );
                 System.out.println(data);
//{"combine_appid":"wxa93c2e338e253027","combine_mchid":"1580157371","combine_payer_info":{"openid":"oABedwLCRW9f7RDOxGZ_cC0CxZsg"},"sub_orders":[{"mchid":"1580157371","trade_type":"APP","trade_state":"SUCCESS","bank_type":"OTHERS","attach":"2020040918091100001007","success_time":"2020-04-09T18:09:21+08:00","amount":{"total_amount":1,"payer_amount":1,"currency":"CNY","payer_currency":"CNY"},"transaction_id":"4308200102202004096304042752","out_trade_no":"2020040918091100001008","sub_mchid":"1583814691"}],"scene_info":{"device_id":"微信公众号"},"combine_out_trade_no":"2020040918091100001007"}
                 Map<String,Object> back=new HashMap<>();
                 back.put("code",200);
                 back.put("message",null);
                 return new ResponseEntity<Map<String,Object>>(back, HttpStatus.OK);

             }

        } catch (Exception e) {
            e.printStackTrace();
            Map<String,Object> back=new HashMap<>();
            back.put("code",500);
            back.put("message",e.getMessage());
            return new ResponseEntity<Map<String,Object>>(back, HttpStatus.INTERNAL_SERVER_ERROR);
        }
        Map<String,Object> back=new HashMap<>();
        back.put("code",400);
        back.put("message","未知的异常回调");
        return new ResponseEntity<Map<String,Object>>(back, HttpStatus.BAD_REQUEST);
    }



    /**
     * @Author hanguanglei
     * @Description //支付退款申请接口
     * @Date 14:54 2020/4/8
     * @Param
     * @param body
     * @return java.lang.String
     **/

    @RequestMapping("/refundsApply")
    @ResponseBody
    public JSONObject ecommerceRefundsApply(@RequestBody String body) {
        // 带有敏感信息接口,需要平台证书
        try {
            Map<String, Object> result = WxPayApi.v3Execution(
                    RequestMethod.POST,
                    WxDomain.CHINA.toString(),
                    WxApiType.E_COMMERCE_REFUNDS.toString(),
                    wxPayV3Bean.getMchId(),
                    getSerialNumber(),
                    getPlatSerialNumber(),
                    wxPayV3Bean.getKeyPath(),
                    body
            );

            //写入数据库逻辑
            if (MapUtil.getInt(result, "status")==200) {
                JSONObject jsonObject = JSONUtil.parseObj(result.get("body"));
                return jsonObject;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


   /**
    * @Author hanguanglei
    * @Description //支付退款查询接口通过refund_id或者out_refund_no  通过type
    * @Date 15:12 2020/4/8
    * @Param
    * @param body
    * @return java.lang.String
    **/
    @RequestMapping("/refundsQuery/{type}")
    @ResponseBody
    public JSONObject ecommerceRefundsApplyQuery(@RequestBody Map<String,String> body, @PathVariable("type") String type) {
        // 带有敏感信息接口,需要平台证书
        String id="";
        String urlSuffix = "";
        try {
             if ("id".equals(type)) {
                 id = MapUtil.getStr(body, "refund_id");
                 urlSuffix = String.format(WxApiType.QUERY_REFUND.toString(), id);
             }else if (("no".equals(type))){
                 id = MapUtil.getStr(body, "out_refund_no");
                 urlSuffix = String.format(WxApiType.QUERY_REFUNDS_BY_REFUND_NO.toString(), id);
             }
            Map<String, Object> result = WxPayApi.v3Execution(
                    RequestMethod.POST,
                    WxDomain.CHINA.toString(),
                    urlSuffix,
                    wxPayV3Bean.getMchId(),
                    getSerialNumber(),
                    getPlatSerialNumber(),
                    wxPayV3Bean.getKeyPath(),
                    body
            );

            //写入数据库逻辑
            if (MapUtil.getInt(result, "status")==200) {
                JSONObject jsonObject = JSONUtil.parseObj(result.get("body"));
                return jsonObject;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }





    /**
     * @Author hanguanglei
     * @Description //订单支付回调API
     * @Date 11:31 2020/4/8
     * @Param
     * @param body  合单商户订单号
     * @return java.lang.String
     **/
    @RequestMapping("/refundsNotify")
    @ResponseBody
    public ResponseEntity<Map<String,Object>> ecommerceRefundsNotify(@RequestBody Map<String,Object> body) {

        try {
            MapUtil.getStr(body, "event_type");
            String event_type=MapUtil.getStr(body, "event_type");

            if ("REFUND.SUCCESS".equals(event_type)){
                String ob=JsonTool.mapToJson(body.get("resource"));
                Map<String,Object> resource= JsonTool.jsonStringToMapObjectStr(ob);
                String associatedData = MapUtil.getStr(resource,"associated_data");
                String nonce = MapUtil.getStr(resource,"nonce");
                String cipherText = MapUtil.getStr(resource,"ciphertext");
                AesUtil aesUtil = new AesUtil(wxPayV3Bean.getApiKey3().getBytes(StandardCharsets.UTF_8));
                // 平台证书密文解密数据
                String data = aesUtil.decryptToString(
                        associatedData.getBytes(StandardCharsets.UTF_8),
                        nonce.getBytes(StandardCharsets.UTF_8),
                        cipherText
                );


                Map<String,Object> back=new HashMap<>();
                back.put("code","SUCCESS");
                back.put("message",null);
                return new ResponseEntity<Map<String,Object>>(back, HttpStatus.OK);

            }

        } catch (Exception e) {
            e.printStackTrace();
            Map<String,Object> back=new HashMap<>();
            back.put("code",500);
            back.put("message",e.getMessage());
            return new ResponseEntity<Map<String,Object>>(back, HttpStatus.INTERNAL_SERVER_ERROR);
        }
        Map<String,Object> back=new HashMap<>();
        back.put("code",400);
        back.put("message","未知的异常回调");
        return new ResponseEntity<Map<String,Object>>(back, HttpStatus.BAD_REQUEST);
    }





    /**
     * @Author hanguanglei
     * @Description //二级商户账户余额实时查询
     * @Date 16:42 2020/4/8
     * @Param
     * @param submchid  二级商户号
     * @return java.lang.String
     **/
    @RequestMapping("/submachid/balance/{submchid}")
    @ResponseBody
    public JSONObject balanceBySubmchId(@PathVariable("submchid") String submchid) {
        try {
            String urlSuffix = String.format(WxApiType.QUERY_BALANCE.toString(), submchid);
            Map<String, Object> result = WxPayApi.v3Execution(
                    RequestMethod.GET,
                    WxDomain.CHINA.toString(),
                    urlSuffix,
                    wxPayV3Bean.getMchId(),
                    getSerialNumber(),
                    getPlatSerialNumber(),
                    wxPayV3Bean.getKeyPath(),
                    ""
            );
            if (MapUtil.getInt(result, "status")==200) {
                JSONObject jsonObject = JSONUtil.parseObj(result.get("body"));
                return jsonObject;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }



    /**
     * @Author hanguanglei
     * @Description //二级商户账户余额终日查询
     * @Date 16:43 2020/4/8
     * @Param
     * @param body
     * @param submchid 二级商户号
     * @return java.lang.String
     **/
    @RequestMapping("/submachid/enddaybalance/{submchid}")
    @ResponseBody
    public JSONObject endDaybalanceBySubmchId(@RequestBody Map<String,Object> body,@PathVariable("submchid") String submchid) {
        try {

            String urlSuffix = String.format(WxApiType.QUERY_ENDDAY_BALANCE.toString(), submchid);
            Map<String, Object> result = WxPayApi.v3Execution(
                    RequestMethod.GET,
                    WxDomain.CHINA.toString(),
                    urlSuffix,
                    wxPayV3Bean.getMchId(),
                    getSerialNumber(),
                    getPlatSerialNumber(),
                    wxPayV3Bean.getKeyPath(),
                    JSONUtil.toJsonStr(body)
            );
            if (MapUtil.getInt(result, "status")==200) {
                JSONObject jsonObject = JSONUtil.parseObj(result.get("body"));
                return jsonObject;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }




    /**
     * @Author hanguanglei
     * @Description //电商平台商户账户余额实时查询
     * @Date 16:42 2020/4/8
     * @ParamBASIC：基本账户
     * OPERATION：运营账户
     * FEES：手续费账户
     * @param accounttype 账户类型
     * @return java.lang.String
     **/
    @RequestMapping("/merchant/balance/{accounttype}")
    @ResponseBody
    public JSONObject balanceByAccounttype(@PathVariable("accounttype") String accounttype) {
        try {
            String urlSuffix = String.format(WxApiType.QUERY_MERCHANT_BALANCE.toString(), accounttype);
            Map<String, Object> result = WxPayApi.v3Execution(
                    RequestMethod.GET,
                    WxDomain.CHINA.toString(),
                    urlSuffix,
                    wxPayV3Bean.getMchId(),
                    getSerialNumber(),
                    getPlatSerialNumber(),
                    wxPayV3Bean.getKeyPath(),
                    ""
            );
            if (MapUtil.getInt(result, "status")==200) {
                JSONObject jsonObject = JSONUtil.parseObj(result.get("body"));
                return jsonObject;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }



    /**
     * @Author hanguanglei
     * @Description //电商平台商户账户余额终日查询
     * @Date 16:43 2020/4/8
     * @Param
     * @param body
     * @param accounttype 账户类型BASIC：基本账户
     * OPERATION：运营账户
     * FEES：手续费账户
     * @return java.lang.String
     **/
    @RequestMapping("/merchant/enddaybalance/{accounttype}")
    @ResponseBody
    public JSONObject endDaybalanceByAccounttype(@RequestBody Map<String,Object> body,@PathVariable("accounttype") String accounttype) {
        try {

            String urlSuffix = String.format(WxApiType.QUERY_MERCHANT_ENDDAY_BALANCE.toString(), accounttype);
            Map<String, Object> result = WxPayApi.v3Execution(
                    RequestMethod.GET,
                    WxDomain.CHINA.toString(),
                    urlSuffix,
                    wxPayV3Bean.getMchId(),
                    getSerialNumber(),
                    getPlatSerialNumber(),
                    wxPayV3Bean.getKeyPath(),
                    JSONUtil.toJsonStr(body)
            );
            if (MapUtil.getInt(result, "status")==200) {
                JSONObject jsonObject = JSONUtil.parseObj(result.get("body"));
                return jsonObject;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }




   /**
    * @Author hanguanglei
    * @Description //二级商户提现
    * @Date 17:09 2020/4/8
    * @Param
    * @param body
    * @return java.lang.String
    **/

    @RequestMapping("/submachid/withdraw")
    @ResponseBody
    public JSONObject withdrawBySubmchId(@RequestBody Map<String,Object> body) {
        try {
            Map<String, Object> result = WxPayApi.v3Execution(
                    RequestMethod.POST,
                    WxDomain.CHINA.toString(),
                    WxApiType.WITHDRAW.toString(),
                    wxPayV3Bean.getMchId(),
                    getSerialNumber(),
                    getPlatSerialNumber(),
                    wxPayV3Bean.getKeyPath(),
                    JSONUtil.toJsonStr(body)
            );
            if (MapUtil.getInt(result, "status")==200) {
                JSONObject jsonObject = JSONUtil.parseObj(result.get("body"));
                return jsonObject;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }



    /**
     * @Author hanguanglei
     * @Description //二级商户账户提现状态查询
     * @Date 16:43 2020/4/8
     * @Param
     * @param body
     * @param withdrawid 微信支付提现单号
     * @return java.lang.String
     **/
    @RequestMapping("/submachid/withdrawStatus/{withdrawid}")
    @ResponseBody
    public JSONObject withdrawStatusBySubmchId(@RequestBody Map<String,String> body, @PathVariable("withdrawid") String withdrawid) {
        try {

            String urlSuffix = String.format(WxApiType.QUERY_WITHDRAW.toString(), withdrawid);
            Map<String, Object> result = WxPayApi.v3Execution(
                    RequestMethod.GET,
                    WxDomain.CHINA.toString(),
                    urlSuffix,
                    wxPayV3Bean.getMchId(),
                    getSerialNumber(),
                    getPlatSerialNumber(),
                    wxPayV3Bean.getKeyPath(),
                    body
            );
            if (MapUtil.getInt(result, "status")==200) {
                JSONObject jsonObject = JSONUtil.parseObj(result.get("body"));
                return jsonObject;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }




    /**
     * @Author hanguanglei
     * @Description //电商平台商户账户提现
     * @Date 16:42 2020/4/8
     * @ParamBASIC：基本账户
     * OPERATION：运营账户
     * FEES：手续费账户
     * @param  body 账户类型
     * @return java.lang.String
     **/
    @RequestMapping("/merchant/withdraw")
    @ResponseBody
    public JSONObject withdrawByAccounttype(@RequestBody Map<String,Object> body) {
        try {
            Map<String, Object> result = WxPayApi.v3Execution(
                    RequestMethod.POST,
                    WxDomain.CHINA.toString(),
                    WxApiType.MERCHANT_WITHDRAW.toString(),
                    wxPayV3Bean.getMchId(),
                    getSerialNumber(),
                    getPlatSerialNumber(),
                    wxPayV3Bean.getKeyPath(),
                    JSONUtil.toJsonStr(body)
            );
            if (MapUtil.getInt(result, "status")==200) {
                JSONObject jsonObject = JSONUtil.parseObj(result.get("body"));
                return jsonObject;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }



    /**
     * @Author hanguanglei
     * @Description //电商平台商户账户余额终日查询
     * @Date 16:43 2020/4/8
     * @Param
     * @param
     * @param outrequestno 商户提现单号
     * @return java.lang.String
     **/
    @RequestMapping("/merchant/withdrawStatus/{outrequestno}")
    @ResponseBody
    public JSONObject withdrawStatusByAccounttype(@PathVariable("outrequestno") String outrequestno) {
        try {

            String urlSuffix = String.format(WxApiType.QUERY_MERCHANT_WITHDRAW.toString(), outrequestno);
            Map<String, Object> result = WxPayApi.v3Execution(
                    RequestMethod.GET,
                    WxDomain.CHINA.toString(),
                    urlSuffix,
                    wxPayV3Bean.getMchId(),
                    getSerialNumber(),
                    getPlatSerialNumber(),
                    wxPayV3Bean.getKeyPath(),
                    ""
            );
            if (MapUtil.getInt(result, "status")==200) {
                JSONObject jsonObject = JSONUtil.parseObj(result.get("body"));
                return jsonObject;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @RequestMapping("/bill/tradebill")
    @ResponseBody
    public AjaxResult tradebill(HttpServletResponse response, @RequestBody Map<String,String> body) {
        try {

            Map<String, Object> result = WxPayApi.v3Execution(
                    RequestMethod.GET,
                    WxDomain.CHINA.toString(),
                    WxApiType.TRADE_BILL.toString(),
                    wxPayV3Bean.getMchId(),
                    getSerialNumber(),
                    getPlatSerialNumber(),
                    wxPayV3Bean.getKeyPath(),
                    body
            );


            Map<String,Object> map=JSONUtil.parseObj(result.get("body"));
           // ByteArrayInputStream bs= (ByteArrayInputStream) downloadBill(map.get("download_url").toString());

            StringBuffer stringBuffer=new StringBuffer() ;
            stringBuffer.append(downloadBill(map.get("download_url").toString()));

            BufferedReader br = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(stringBuffer.toString().getBytes(Charset.forName("utf8"))), Charset.forName("utf8")));
            String line;
            int len=0;
            StringBuffer strbuf=new StringBuffer();
            List<BillOrderDetail> list=new ArrayList<>();
            while ( (line = br.readLine()) != null ) {
                if(!line.trim().equals("")){
                    if (len>0){
                       // line="<br1>"+line;//每行可以做加工
                      //  strbuf.append(line+"\r\n");
                        list.add(dataDetail(line));
                    }else {
                        line="<br>"+line;//每行可以做加工
                        strbuf.append(line+"\r\n");

                    }
                    len++;

                }
            }

            System.out.println(list.get(2).getPayTime());
            Map<String, String> headers=new HashMap<>();
            headers.put("Content-type","application/octet-stream");
            response.setHeader("content-type", "application/octet-stream");
            response.setContentType("application/octet-stream");
            // 下载文件能正常显示中文
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("账单下载", "UTF-8"));
            // 实现文件下载


//            OutputStream out=null;
//            InputStream is=null;
//            try {
//               out = response.getOutputStream();
//                is = bs;
//                byte[] buff = new byte[1024];
//                int len = 0;
//                while((len=is.read(buff))!=-1){
//                    out.write(buff, 0, len);
//                }
//                is.close();
//                out.close();
//                System.out.println("Download  successfully!");
//
//            } catch (Exception e) {
//                System.out.println("Download  failed!");
//                return AjaxResult.error();
//
//            } finally {
//                if (is != null) {
//                    try {
//                        out.close();
//                    } catch (IOException e) {
//                        e.printStackTrace();
//                    }
//                }
//                if (is != null) {
//                    try {
//                        out.close();
//                    } catch (IOException e) {
//                        e.printStackTrace();
//                    }
//                }
//            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }




    public BillOrderDetail dataDetail(String strb){
        String[] data=strb.split(",");
        BillOrderDetail billOrderDetail=new BillOrderDetail();
        if (data.length>7) {
            //交易时间
            billOrderDetail.setPayTime(data[0]);
            //公众账号ID
            billOrderDetail.setWxId(data[1]);
            //商户号
            billOrderDetail.setMachId(data[2]);
            //特约商户号
            billOrderDetail.setSubMachId(data[3]);
            //设备号
            billOrderDetail.setDeviceId(data[4]);
            //微信订单号
            billOrderDetail.setWxOderNo(data[5]);
            //商户订单号
            billOrderDetail.setMachOrderNo(data[6]);
            //用户标识
            billOrderDetail.setUserId(data[7]);
            //交易类型
            billOrderDetail.setPayType(data[8]);
            //交易状态
            billOrderDetail.setPayStatus(data[9]);
            //付款银行
            billOrderDetail.setAccoutBank(data[10]);
            //货币种类
            billOrderDetail.setCurrency(data[11]);
            //应结订单金额
            billOrderDetail.setShoudMoney(data[12]);
            //代金券金额
            billOrderDetail.setCouponMoney(data[13]);
            //微信退款单号
            billOrderDetail.setWxRefundOrderNo(data[14]);
            //商户退款单号
            billOrderDetail.setMachRefundOrderNO(data[15]);
            //退款金额
            billOrderDetail.setRefundMoney(data[16]);
            //充值券退款金额
            billOrderDetail.setCouponRefundMoney(data[17]);
            //退款类型
            billOrderDetail.setRefundType(data[18]);
            //退款状态
            billOrderDetail.setRefundStatus(data[19]);
            //商品名称
            billOrderDetail.setGoodsName(data[20]);
            //商户数据包
            billOrderDetail.setMachData(data[21]);
            //手续费
            billOrderDetail.setServiceMoney(data[22]);
            //费率
            billOrderDetail.setServiceRate(data[23]);
            //订单金额
            billOrderDetail.setOrderMoney(data[24]);
            //申请退款金额
            billOrderDetail.setApplyRefundMoney(data[25]);
            //费率备注
            billOrderDetail.setRateMemo(data[26]);
        }else {
            //交易时间
            billOrderDetail.setPayTime(data[0]);
            //公众账号ID
            billOrderDetail.setWxId(data[1]);
            //商户号
            billOrderDetail.setMachId(data[2]);
            //特约商户号
            billOrderDetail.setSubMachId(data[3]);
            //设备号
            billOrderDetail.setDeviceId(data[4]);
            //微信订单号
            billOrderDetail.setWxOderNo(data[5]);
            //商户订单号
            billOrderDetail.setMachOrderNo(data[6]);
        }

        return billOrderDetail;

    }



    public Object downloadBill(String url){
        try {
            Map<String,Object> result = WxPayApi.v3ExecutionDownLoad(
                    RequestMethod.DOWNLOAD,
                    WxDomain.CHINA.toString(),
                    url.replace(WxDomain.CHINA.toString(), ""),
                    wxPayV3Bean.getMchId(),
                    getSerialNumber(),
                    getPlatSerialNumber(),
                    wxPayV3Bean.getKeyPath(),""
            );


            System.out.println(result.get("body").toString());
//            HttpGet httpGet = new HttpGet(url);
//
//            System.out.println(url);
//
//            // NOTE: 建议指定charset=utf-8。低于4.4.6版本的HttpCore，不能正确的设置字符集，可能导致签名错误
//
//            httpGet.addHeader("Charset", "UTF-8");
//            httpGet.addHeader("Accept", "application/json");
//            httpGet.addHeader("Wechatpay-Serial",getPlatSerialNumber());
//
//            CloseableHttpResponse response = new WxHttp(wxPayV3Bean,getSerialNumber()).bulid().execute(httpGet);
//            assertTrue(response.getStatusLine().getStatusCode() != 401);
//            try {
//                HttpEntity entity2 = response.getEntity();
//                // do something useful with the response body
//                // and ensure it is fully consumed
//               entity2.getContent();
//
//                EntityUtils.consume(entity2);
//                return entity2.getContent();
//            } catch (IOException e) {
//                e.printStackTrace();
//            } finally {
//                response.close();
//            }

            if (MapUtil.getInt(result, "status")==200) {

                return result.get("body");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }




    @RequestMapping("/cipher")
    @ResponseBody
    public String cipher() {
        try {
            // 敏感信息加密
            X509Certificate certificate = PayKit.getCertificate(FileUtil.getInputStream(wxPayV3Bean.getPlatformCertPath()));
            String encrypt = PayKit.rsaEncryptOAEP("IJPay", certificate);
            System.out.println(encrypt);
            // 敏感信息解密
            String encryptStr = encrypt;
            PrivateKey privateKey = PayKit.getPrivateKey(wxPayV3Bean.getKeyPath());
            String decrypt = PayKit.rsaDecryptOAEP(encryptStr, privateKey);
            System.out.println(decrypt);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
